# 
#     Boid : Define a basic boid
# 
import c3d
import PyCarrara
from PyCarrara import *
from mindset import MindSet
from CarraraObject import CarraraObject
from math import *

class Boid(CarraraObject): 
  def __init__(self,min_speed=2.0,max_speed=5.0,max_acceleration=5.0,mindset=None,velocity = Vector(0.0,0.0,0.0),max_turn=None,**args):
    super(Boid,self).__init__(**args)
    self.mindset = mindset
    self.mindset.boid = self
    self.max_speed = max_speed
    self.min_speed = min_speed
    self.max_acceleration = max_acceleration
    self.velocity = velocity
    if self.scene is not None:
      if self.mindset is not None:
        self.mindset.scene = self.scene
    self.max_turn = max_turn
#       
#    Make the current move
#        
  def prepare_move(self):
    self.acceleration = self.mindset.move()
    self.acceleration = self.clamp_max(self.acceleration,self.max_acceleration)
    if self.max_turn is not None:
      self.acceleration = self.clamp_turn(self.acceleration,self.max_turn/self.scene.fps)

  def move(self):
    self.velocity = self.clamp(self.velocity + self.acceleration,self.min_speed,self.max_speed)
    print self.name,"velocity",(self.velocity * self.scene.fps).mag(),self.velocity * self.scene.fps
    self.position = self.position + self.velocity
		# rotate boid based on current velocity (in z-axis)
		# arctan2 function is used to provide a value between -pi and +pi
    angle = atan2 (self.velocity.y, self.velocity.x) * 180.0 / pi
    self.orientation =  Vector(0.0, 0.0, angle)
    CarraraObject.move(self)
#     
#   Clamp a vector to a given magnitude
#                                   
  def clamp_max(self,vector,magnitude):
    if magnitude /self.scene.fps < vector.mag():
      return vector.unit()*magnitude /self.scene.fps
    return vector  
#     
#   Clamp a vector between a min and max magnitudes
#                                   

  def clamp(self,vector,min_magnitude,max_magnitude):
    if min_magnitude /self.scene.fps > vector.mag():
      return vector.unit() * min_magnitude /self.scene.fps
    if max_magnitude /self.scene.fps < vector.mag():
      return vector.unit() * max_magnitude /self.scene.fps
    return vector  
    
##########################################################################################
# Rule 5: MAX_TURN -
# Before changing the velocity vector, limit the heading change possible. The result of
# this function is to return a vector with the same speed (vector length), but restricting
# the angle (heading) to not exceed the "max_turning" value.
# This is done by computing the heading change, comparing it to the max amount allowed,
# and reducing the heading change if it exceeds that max amount
# NOTE: For now, only the (x,y) plane is considered in this calculation
# This function checks the current heading to make sure it is not beyond what it can do.
# If so, it reduces the heading change.
#=========================================================================================
  def clamp_turn(self,velocity_change,max_turning):
    heading_change = degrees(atan2(velocity_change.y, velocity_change.x))
    if abs(heading_change) > max_turning :
      print "heading_change",velocity_change,heading_change,max_turning
      if heading_change < 0:
        new_heading = -max_turning 
      else:
        new_heading = max_turning
      print heading_change
      vector_mag = sqrt(velocity_change.x**2 + velocity_change.y**2)
      velocity_change.y = vector_mag * cos(new_heading * pi / 180)
      velocity_change.x = vector_mag * sin(new_heading * pi / 180)
      print "new heading_change",new_heading,velocity_change
    return velocity_change

  
    